package com.summer.common.lib.core.servlets;

import com.summer.common.lib.context.WebContext;
import com.summer.common.lib.core.exception.HibernateValidatorException;
import com.summer.common.lib.core.handles.HandlerMethodInfo;
import com.summer.common.lib.core.interfaces.Controller;
import com.summer.common.lib.core.interfaces.PageController;
import com.summer.common.lib.core.interfaces.RestController;
import org.apache.commons.lang.StringUtils;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static java.util.Arrays.asList;
import static org.apache.commons.lang.StringUtils.substringAfter;

/**
 * 视图层Servlet
 */
public class SummerControllerServlet extends HttpServlet {

    /**
     * 请求路径与Controller的映射关系缓存
     */
    private Map<String, Controller> controllerMapping = new ConcurrentHashMap<>();

    /**
     * 请求路径和 handlerMethodInfo的关系缓存
     */
    private Map<String, HandlerMethodInfo> handlerMethodInfoMapping = new ConcurrentHashMap<>();

    /**
     * 读取web.xml配置
     *
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.initControllerContainer(config);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 解析请求url, 获取对应的controller 实现
        String requestURI = req.getRequestURI();
        String contextPath = req.getContextPath();
        String requestMappingPath = substringAfter(requestURI,
                StringUtils.replace(contextPath, "//", "/"));
        // get Controller
        Controller controller = controllerMapping.get(requestMappingPath);
        if (controller != null) {
            HandlerMethodInfo handlerMethodInfo = handlerMethodInfoMapping.get(requestMappingPath);
            if (handlerMethodInfo != null) {
                String method = req.getMethod();
                if (!handlerMethodInfo.getSupportedHttpMethods().contains(method)) {
                    // HTTP 方法不支持
                    resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                    return;
                }
                try {
                    // v1 不做 rest 解析
                    if (controller instanceof PageController) {
                        PageController pageController = (PageController) controller;
                        String pagePath = null;

                        pagePath = pageController.execute(req, resp);
                        ServletContext servletContext = req.getServletContext();
                        if (!pagePath.startsWith("/")) {
                            pagePath = "/" + pagePath;
                        }
                        RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher(pagePath);
                        requestDispatcher.forward(req, resp);

                    } else if (controller instanceof RestController) {
                        RestController restController = (RestController) controller;
                        Object execute = restController.execute(req);
                        resp.getWriter().print(execute);
                    }

                } catch (Throwable throwable) {
                    if (throwable.getCause() instanceof IOException) {
                        throw (IOException) throwable.getCause();
                    } else if (throwable instanceof HibernateValidatorException) {
                        resp.getWriter().print(throwable.getMessage());
                    } else {
                        throw new ServletException(throwable.getCause());
                    }
                }
            }
        }
    }

    /**
     * 初始化加载所有controller
     * 使用SPI 加载所有控制层
     * 2021-03-10 优化
     * 从注入Bean中注册controller
     */
    private void initControllerContainer(ServletConfig config) {
        // 1 加载所有实现controller接口的实现类
        // 从 ServletContext中获取所有注入的bean
        Map<String, Object> allComponents = WebContext.getInstantiate().getAllComponents();

        Collection<Object> values = allComponents.values();

        values.forEach(component -> {
            if (component instanceof Controller) {
                Class<? extends Controller> controllerClass = ((Controller) component).getClass();
                // 获取类名 Path标注
                Path clazzPathAnnotation = controllerClass.getAnnotation(Path.class);
                // 获取类名path标注的路径信息
                StringBuilder requestPath = new StringBuilder(clazzPathAnnotation.value());
                // 获取控制层下的所有可用方法
                Method[] methods = controllerClass.getMethods();
                for (Method method : methods) {
                    // 解析方法
                    Set<String> httpMethods = getSupportHttpMethods(method);
                    // 获取方法名的Path标注
                    Path methodPathAnnotation = method.getAnnotation(Path.class);
                    if (methodPathAnnotation != null) {
                        requestPath.append(methodPathAnnotation.value());
                    }
                    handlerMethodInfoMapping.put(
                            requestPath.toString(), new HandlerMethodInfo(requestPath.toString(), method, httpMethods));
                }
                controllerMapping.put(requestPath.toString(), (Controller) component);
            }
        });

/*
        ServiceLoader<Controller> load = ServiceLoader.load(Controller.class);

        load.forEach(controller ->

        {
            // 返回此Object的运行时类
            Class<? extends Controller> controllerClass = controller.getClass();
            // 获取类名 Path标注
            Path clazzPathAnnotation = controllerClass.getAnnotation(Path.class);
            // 获取类名path标注的路径信息
            StringBuilder requestPath = new StringBuilder(clazzPathAnnotation.value());
            // 获取控制层下的所有可用方法
            Method[] methods = controllerClass.getMethods();
            for (Method method : methods) {
                // 解析方法
                Set<String> httpMethods = getSupportHttpMethods(method);
                // 获取方法名的Path标注
                Path methodPathAnnotation = method.getAnnotation(Path.class);
                if (methodPathAnnotation != null) {
                    requestPath.append(methodPathAnnotation.value());
                }

                handlerMethodInfoMapping.put(
                        requestPath.toString(), new HandlerMethodInfo(requestPath.toString(), method, httpMethods));
            }
            controllerMapping.put(requestPath.toString(), controller);
        });
    */
    }

    /**
     * 从方法签名中获取 httpMethod 标注的支持类型
     *
     * @return
     */
    private Set<String> getSupportHttpMethods(Method method) {
        Set<String> supportHttpMethods = new LinkedHashSet<>();
        for (Annotation annotationFromMethod : method.getAnnotations()) {
            HttpMethod httpMethod = annotationFromMethod.annotationType().getAnnotation(HttpMethod.class);
            if (httpMethod != null) {
                supportHttpMethods.add(httpMethod.value());
            }
        }
        if (supportHttpMethods.isEmpty()) {
            supportHttpMethods.addAll(
                    asList(
                            HttpMethod.GET,
                            HttpMethod.POST,
                            HttpMethod.PUT,
                            HttpMethod.DELETE,
                            HttpMethod.HEAD,
                            HttpMethod.OPTIONS
                    ));
        }
        return supportHttpMethods;
    }


}
